import * as React from 'react';
import { Set, Rect, Text, Path } from 'react-raphael';
import { INode } from '../model/Model';
import { Point, Range, OperaType } from '../../reducer/const';
import { checkPointInRange } from '../../util/eventUtil';
import { ICON, COLOR } from './Style';

export interface INodeProps extends INode {
    operaType: number;
    selectId?: string;
    onChange: Function;
    onLinkedEnd: (startId: string, point: Point) => void;
}

export default class TaskNode extends React.Component<INodeProps, any> {
    public refs: {
        select: any;
        rect: any;
        rectPath: any;
        text: any;
        icon: any;
    };

    protected colorStroke;
    protected colorFill;
    protected colorIcon;
    protected strokeWidth;
    protected strokeIconWidth;
    protected iconWidth = 0;

    private drag = false;
    private dragPosition: Point;
    private xx: number;
    private yy: number;
    private dx: number;
    private dy: number;


    constructor(props: INodeProps) {
        super(props);
        const {id, range, name, type, icon, operaType, selectId, history} = this.props;
        this.state = { id, range, name, type, icon, operaType, selectId, history };
        if (history) {
            this.colorStroke = COLOR.stroke2;
            this.colorIcon = COLOR.icon2;
            this.colorFill = COLOR.fill2;
            this.strokeWidth = 3;
            this.strokeIconWidth = 4;
        } else {
            this.colorStroke = COLOR.stroke1;
            this.colorIcon = COLOR.stroke1;
            this.colorFill = COLOR.fill1;
            this.strokeWidth = 1;
            this.strokeIconWidth = 1;
        }
    }

    handleMove = (dx, dy, x, y, e) => {
        const {operaType, history} = this.props;
        if (OperaType.Drag !== operaType || !this.drag || history) {
            return;
        }
        if (this.props.onChange) {
            const {id, range} = this.props;
            this.dragPosition = {
                x: this.xx + dx + this.dx,
                y: this.yy + dy + this.dy
            };
            this.props.onChange(e, id, {
                key: 'dragMove',
                value: this.dragPosition
            });
            this.updateSelect(this.dragPosition);
            this.updateRect(this.dragPosition);
            this.updateText(this.dragPosition, range);
            this.updateIcon(this.dragPosition, range);
            this.updateOtherElement(this.dragPosition, range);
        }
        this.drag = true;
    }


    updateOtherElement = (point: Point, range: Range) => {
        // nothing todo
    }

    updateText = (point: Point, range: Range) => {
        const text = this.getRefs().text && this.getRefs().text.getElement();
        if (text) {
            text.attr({
                x: point.x + range.width / 2,
                y: point.y + range.height / 2
            });
        }
    }

    updateSelect = (point: Point) => {
        const select = this.getRefs().select && this.getRefs().select.getElement();
        if (select) {
            select.attr({
                x: point.x - 2,
                y: point.y - 2
            });
        }
    }
    updateRect = (point: Point) => {
        const rect = this.getRefs().rect && this.getRefs().rect.getElement();
        if (rect) {
            rect.attr(point);
        }
    }

    getRefs = () => {
        return this.refs;
    }

    handleStart = (x, y, e) => {
        const {operaType, history} = this.props;
        if (OperaType.Drag !== operaType || history) {
            return;
        }
        this.drag = true;
        let rx;
        let ry;
        if (this.getRefs().rect) {
            const rect = this.getRefs().rect.getElement();
            rx = rect.attr('x');
            ry = rect.attr('y');
        } else if (this.getRefs().select) {
            const select = this.getRefs().select.getElement();
            rx = select.attr('x') - 2;
            ry = select.attr('y') - 2;
        }
        this.xx = x;
        this.yy = y;
        this.dx = rx - x;
        this.dy = ry - y;
    }

    handleEnd = (e) => {
        const {operaType, id, range, history} = this.props;
        if (history) {
            return;
        }
        if (OperaType.Drag === operaType) {
            this.drag = false;
            this.props.onChange(e, id, {
                key: 'dragEnd',
                value: Object.assign({
                    width: range.width,
                    height: range.height
                }, this.dragPosition)
            });
        } else if (OperaType.Linked === operaType) {
            const point: Point = new Point(e.layerX, e.layerY);
            if (checkPointInRange(range, point)) {
                console.log('Linked error,the start node and the end node can not the same node');
            } else {
                this.props.onLinkedEnd(id, point);
            }
        }
    }


    handleClick = (e) => {
        if (this.drag) {
            return;
        }
        const {selectId, id} = this.props;
        const selected = id === selectId;
        if (this.props.onChange) {
            this.props.onChange(e, id, {
                key: 'selectNode',
                value: selected ? null : id
            });
        }
    }


    render() {
        return (
            <Set>
                {this.renderSelect()}
                {this.renderRect()}
                {this.renderIcon()}
                {this.renderText()}
            </Set >
        );
    }


    renderIcon(): JSX.Element {
        this.iconWidth = 40;
        const {range, id, selectId, operaType} = this.state;
        const selected = id === selectId;
        let cursorAttr = selected ? { 'cursor': 'default', 'z-index': 11 } : { 'cursor': 'default', 'z-index': 10 };
        if (!history) {
            if (OperaType.Linked === operaType) {
                cursorAttr.cursor = 'crosshair';
            } else if (selectId === id) {
                cursorAttr.cursor = 'move';
            }
        }
        let iconProps = this.getIconProps(range);
        let iconAttr = this.getIconAttr();
        iconAttr = Object.assign(iconAttr, cursorAttr);
        return <Rect ref='icon' {...iconProps} attr={iconAttr} ></Rect>;

    }

    getIconAttr() {
        const iconAttr = { 'fill': `${this.colorIcon}`, 'stroke': `${this.colorIcon}`, 'stroke-width': `${this.strokeIconWidth}` };
        return iconAttr;
    }

    getIconProps(range: Range) {
        const height = this.iconWidth * 0.4;
        const width = this.iconWidth * 0.6;
        return {
            x: range.x + this.iconWidth * 0.2,
            y: range.y + this.iconWidth * 0.3,
            width: width,
            height: height,
            r: height * .3
        };
    }

    updateIcon = (point: Point, range: Range): void => {
        const icon = this.getRefs().icon && this.getRefs().icon.getElement();
        if (icon) {
            const iconProps = this.getIconProps(range);
            icon.attr(iconProps);
        }
    }

    renderText() {
        const {range, operaType} = this.state;
        const {name, selectId, id} = this.props;
        const selected = id === selectId;
        let textAttr = { 'font-size': '16px' };
        let cursorAttr = selected ? { 'cursor': 'default', 'z-index': 11 } : { 'cursor': 'default', 'z-index': 10 };
        if (!history) {
            if (OperaType.Linked === operaType) {
                cursorAttr.cursor = 'crosshair';
            } else if (selectId === id) {
                cursorAttr.cursor = 'move';
            }
        }
        textAttr = Object.assign(textAttr, cursorAttr);
        let textProps: Point = new Point(range.x + this.iconWidth + (range.width - this.iconWidth) / 2, range.y + range.height / 2);
        return <Text ref='text' {...textProps} text={name} attr={textAttr} {...this.getEventProps() }></Text>;
    }

    renderRect() {
        const {range} = this.state;
        const {selectId, id, operaType, history} = this.props;
        const selected = id === selectId;

        let cursorAttr = selected ? { 'cursor': 'default', 'z-index': 11 } : { 'cursor': 'default', 'z-index': 10 };
        if (!history) {
            if (OperaType.Linked === operaType) {
                cursorAttr.cursor = 'crosshair';
            } else if (selectId === id) {
                cursorAttr.cursor = 'move';
            }
        }
        let rectAttr = { 'stroke': `${this.colorStroke}`, 'fill': `${this.colorFill}`, 'stroke-width': `${this.strokeWidth}` };
        rectAttr = Object.assign(rectAttr, cursorAttr);
        return <Rect ref='rect' {...range} attr={rectAttr} {...this.getEventProps() }></Rect>;
    }

    getEventProps() {
        const {history} = this.props;
        if (history) {
            return {};
        } else {
            return {
                drag: {
                    move: this.handleMove,
                    start: this.handleStart,
                    end: this.handleEnd
                },
                click: this.handleClick
            };
        }
    }

    renderSelect() {
        const { selectId, id, history, operaType} = this.props;
        if (history) {
            return null;
        } else {
            const {range} = this.state;
            const selected = id === selectId && OperaType.Drag === operaType;
            let cursorAttr = selected ? { 'cursor': 'default', 'z-index': 11 } : { 'cursor': 'default', 'z-index': 10 };
            // let selectProps = { x: range.x - 2, y: range.y - 2, width: range.width + 4, height: range.height + 4, r: range.r };
            let selectProps = { x: range.x - 2, y: range.y - 2, width: range.width + 4, height: range.height + 4 };
            let selectAttr = { 'stroke': 'red', 'stroke-width': 2, 'stroke-dasharray': '- ' };
            selectAttr = Object.assign(selectAttr, cursorAttr);
            return <Rect ref='select' {...selectProps} attr={selectAttr} hide={!selected}  {...this.getEventProps() }></Rect>;
        }
    }
}
